#pragma once

#include <semaphore.h>
#include <pthread.h>
#include <iostream>
#include <vector>
#include <cassert>

static const int gcap = 5; // global capacity

template <class T>
class RingQueue
{
private:
    void P(sem_t *sem)
    {
        int n = sem_wait(sem);
        assert(n == 0);//建议if
        (void)n;
    }
    void V(sem_t *sem)
    {
        int n = sem_post(sem);
        assert(n == 0);//建议if
        (void)n;
    }
public:
    RingQueue(const int &cap = gcap)
        :_queue(cap)
        ,_cap(cap)
    {
        int n = sem_init(&_spaceSem, 0, _cap);//刚开始ringqueue是空的
        // int n = sem_init(&_spaceSem, 0, 5);不要这么写，wyn你个蠢货
        assert(n == 0);
        n = sem_init(&_dataSem, 0, 0);
        assert(n == 0);

        _pStep = _cStep = 0;//刚开始队列为空

        pthread_mutex_init(&_pmutex, nullptr); // attribute--属性
        pthread_mutex_init(&_cmutex, nullptr);
    }
    void Push(const T& in)
    {
        P(&_spaceSem);//申请到了空间信号量，意味着可以进行push操作，但应该往哪里push呢？所以需要确定push的位置下标

        //多个生产者来的时候，你们先竞争一下锁
        pthread_mutex_lock(&_pmutex);
        _queue[_pStep++] = in;
        _pStep %= _cap;
        pthread_mutex_unlock(&_pmutex);

        V(&_dataSem);//空间资源没有变，但是多了数据资源
    }
    void Pop(T *out)
    {
        //应该先申请信号量，再去竞争锁
        P(&_dataSem);

        pthread_mutex_lock(&_cmutex);
        *out = _queue[_cStep++];
        _cStep %= _cap;
        pthread_mutex_unlock(&_cmutex);

        V(&_spaceSem);
    }
    ~RingQueue()
    {
        sem_destroy(&_spaceSem);
        sem_destroy(&_dataSem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }
private:
    std::vector<T> _queue;
    int _cap;         // capacity容量
    //通过spaceSem和dataSem两者实现了生产线程和消费线程之间的同步，此时的信号量代替了条件变量的作用。
    //但是比条件变量更牛的一点是，我们可以事先知道临界资源的使用情况，
    sem_t _spaceSem; // 生产者想生产，看中的是space resource
    sem_t _dataSem;   // 消费者想消费，看中的是data resource
    int _pStep;
    int _cStep;
    pthread_mutex_t _pmutex;
    pthread_mutex_t _cmutex;
};
